package com.dz.risk.service.impl;

import com.dz.risk.common.SysConstant;
import com.dz.risk.common.enums.SystemLogEnum;
import com.dz.risk.common.utils.DateUtils;
import com.dz.risk.common.utils.JepUtils;
import com.dz.risk.constant.IndexConstant;
import com.dz.risk.custom.dynamic.ReportFormsCalculation;
import com.dz.risk.pojo.domain.ReportFormsDataValueEntity;
import com.dz.risk.pojo.domain.SystemLogEntity;
import com.dz.risk.service.IndexDealWith;
import com.dz.risk.service.ReportFormsDataValueService;
import com.dz.risk.service.SystemLogService;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.text.NumberFormat;
import java.util.*;

/**
 * 混合运算类型编码数据获取业务处理类
 *
 * @author Doke
 * @date 2021/9/7 11:16
 */
@Service("IndexDealWith_" + IndexConstant.MIXED_COMPUTING)
public class IndexDealWithZeroImpl implements IndexDealWith {

    private final ApplicationContext applicationContext;

    private final ReportFormsDataValueService reportFormsDataValueService;

    private final Redisson redisson;

    private final SystemLogService systemLogService;

    @Autowired
    public IndexDealWithZeroImpl(ApplicationContext applicationContext, ReportFormsDataValueService reportFormsDataValueService, Redisson redisson, SystemLogService systemLogService) {
        this.applicationContext = applicationContext;
        this.reportFormsDataValueService = reportFormsDataValueService;
        this.redisson = redisson;
        this.systemLogService = systemLogService;
    }

    /**
     * 根据报表单元格信息和计算时间获取指标数据
     *
     * @param reportFormsDataValueEntity 报表单元格信息
     * @param calculatingTime            计算版本
     * @param beginDate                  报表开始时间
     * @param endDate                    报表结束时间
     * @return 数据值
     */
    @Override
    public Double getDataByReportFormsDataValueAndCalculatingTime(ReportFormsDataValueEntity reportFormsDataValueEntity, Date calculatingTime, Date beginDate, Date endDate) {
        Double indexValue = null;
        //设置分布式锁
        RLock lock = redisson.getLock(reportFormsDataValueEntity.getTableCellSerialNumber() + calculatingTime);

        try {
            //上锁
            lock.lock();
            //根据编号以及计算版本查询该编号是否已存在计算数据
            List<ReportFormsDataValueEntity> reportFormsDataValueEntities = reportFormsDataValueService.selectBySerialNumberAndVersion(reportFormsDataValueEntity.getTableCellSerialNumber(), calculatingTime);

            //判断如果该日已有该编号得计算数据则无需在进行公式计算
            if (!CollectionUtils.isEmpty(reportFormsDataValueEntities)) {
                indexValue = Objects.nonNull(reportFormsDataValueEntities.get(0).getDataValue()) ? Double.parseDouble(reportFormsDataValueEntities.get(0).getDataValue()) : null;

                //如果没有则根据该编号得公式计算
            } else {
                //如果公式未设置则不进行公式解析计算
                if (Objects.nonNull(reportFormsDataValueEntity.getFormula())) {
                    //获取公式详情
                    String formulaDetails = reportFormsDataValueEntity.getFormula().getFormulaDetails();
                    //获取参与公式计算的参数集合
                    List<String> parseFormula = reportFormsDataValueEntity.getFormula().getParseFormula();

                    int initialization = (int) (parseFormula.size() / 0.75 + 1);
                    //初始化集合容量
                    Map<String, Double> paramsMap = new HashMap<>(initialization);

                    //根据指标编号以及计算版本设置每个编号对应的值
                    parseFormula.forEach(a -> paramsMap.put(a, getIndexValueByVersionAndIndexId(a, calculatingTime, beginDate, endDate)));

                    //如果参数值中有为NULL的则记录计算时那些值是null
                    if (paramsMap.containsValue(null)) {
                        StringBuilder stringBuilder = new StringBuilder("计算"+reportFormsDataValueEntity.getTableCellSerialNumber()+"时,参与计算中的编码为空值的有 : ");
                        paramsMap.forEach((k,v) -> {
                            if(Objects.isNull(v)){
                                stringBuilder.append(k).append(",");
                            }
                        });
                        //记录日志
                        systemLogService.addStSystemLog(SystemLogEntity
                                .builder()
                                .actionType(SystemLogEnum.CALCULATE.getActionType())
                                .operationContent(stringBuilder.toString())
                                .manipulationMenu(SysConstant.REPORT_MANAGEMENT)
                                .build());
                    }else {
                        //计算公式得出结果值
                        indexValue = JepUtils.parseExpression(formulaDetails, paramsMap);
                    }
                }

                //混合运算类型计算结果后及时保存入库,以供其它引用该编号得公式可直接查询获取,无需再次计算
                Optional.ofNullable(indexValue).ifPresent(v -> reportFormsDataValueEntity.setDataValue(String.valueOf(formatDouble(v))));
                reportFormsDataValueService.save(reportFormsDataValueEntity.setVersion(calculatingTime));
            }
        } finally {
            //解锁
            lock.unlock();
        }

        return indexValue;
    }

    /**
     * 根据指标编号跟计算版本获取对应得指标值
     *
     * @param indexId         指标编号
     * @param calculatingTime 计算版本
     * @param beginDate       报表开始时间
     * @param endDate         报表结束时间
     * @return 指标值
     */
    private Double getIndexValueByVersionAndIndexId(String indexId, Date calculatingTime, Date beginDate, Date endDate) {
        ReportFormsDataValueEntity reportFormsDataValueEntity = new ReportFormsDataValueEntity();
        reportFormsDataValueEntity.setTableCellSerialNumber(indexId);

        //如果编码为混合运算类型,则查询改编码所属的报表是否已生成该版本的模板
        if(IndexConstant.MIXED_COMPUTING.equals(indexId.substring(indexId.length() - 1)) || IndexConstant.SQL_COMPUTING.equals(indexId.substring(indexId.length() - 1))){
            //查询该编号此次计算版本得模板单元格对象
            List<ReportFormsDataValueEntity> dataValueEntities = reportFormsDataValueService.selectByReportFormsVersionAndIndexNumber(indexId, beginDate, endDate, null);

            //如果还未生成该编码得得该月份计算模板则记录日志
            if (CollectionUtils.isEmpty(dataValueEntities)) {
                //记录日志
                systemLogService.addStSystemLog(SystemLogEntity
                        .builder()
                        .actionType(SystemLogEnum.CALCULATE.getActionType())
                        .operationContent("计算"+indexId+"时,"+indexId+"所属报表或该编码还未生成:"+ DateUtils.dateToStringYears(beginDate)+"~"+DateUtils.dateToStringYears(endDate)+"版本,请生成!")
                        .manipulationMenu(SysConstant.REPORT_MANAGEMENT)
                        .build());
            }else {
                reportFormsDataValueEntity = dataValueEntities.get(0);

            }
        }

        ReportFormsCalculation reportFormsCalculation = applicationContext.getBean(ReportFormsCalculation.class);
        return reportFormsCalculation.cellCalculation(reportFormsDataValueEntity, beginDate, endDate, calculatingTime);
    }

    private static String formatDouble(double d) {
        NumberFormat nf = NumberFormat.getInstance();
        //设置保留多少位小数
        nf.setMaximumFractionDigits(4);
        // 取消科学计数法
        nf.setGroupingUsed(false);
        //返回结果
        return nf.format(d);
    }
}
